1 //==============================================================================
2 // file : Base64.java
3 // project: Java Common Utility
4 //
5 // last change: date: $Date: 2003/09/10 09:22:14 $
6 // by: $Author: bitiboy $
7 // revision: $Revision: 1.1 $
8 //------------------------------------------------------------------------------
9 // copyright: GNU GPL Software License (see class documentation)
10 //==============================================================================
11 package com.justhis.util;
12
13
14 //////////////////////license & copyright header/////////////////////////
15 // //
16 // Base64 - encode/decode data using the Base64 encoding scheme //
17 // //
18 // Copyright (c) 1998 by Kevin Kelley //
19 // //
20 // This library is free software; you can redistribute it and/or //
21 // modify it under the terms of the GNU Lesser General Public //
22 // License as published by the Free Software Foundation; either //
23 // version 2.1 of the License, or (at your option) any later version. //
24 // //
25 // This library is distributed in the hope that it will be useful, //
26 // but WITHOUT ANY WARRANTY; without even the implied warranty of //
27 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
28 // GNU Lesser General Public License for more details. //
29 // //
30 // You should have received a copy of the GNU Lesser General Public //
31 // License along with this library; if not, write to the Free Software //
32 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA //
33 // 02111-1307, USA, or contact the author: //
34 // //
35 // Kevin Kelley <kelley@ruralnet.net> - 30718 Rd. 28, La Junta, CO, //
36 // 81050 USA. //
37 // //
38 ////////////////////end license & copyright header///////////////////////
39 import com.justhis.util.exception.UtilException;
40
41 import java.io.*;
42
43
44 // needed only for main() method.
45
46 /***
47 * Provides encoding of raw bytes to base64-encoded characters, and decoding of
48 * base64 characters to raw bytes.
49 *
50 * @author Kevin Kelley
51 * @version 1.3
52 */
53 public class Base64 {
54 //~ Static fields/initializers ---------------------------------------------
55
56 //
57 // code characters for values 0..63
58 //
59
60 /*** TODO */
61 private static char[] alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
62 .toCharArray();
63
64 //
65 // lookup table for converting base64 characters to value in range 0..63
66 //
67
68 /*** TODO */
69 private static byte[] codes = new byte[256];
70
71 static {
72 for (int i = 0; i < 256; i++) {
73 codes[i] = -1;
74 }
75
76 for (int i = 'A'; i <= 'Z'; i++) {
77 codes[i] = (byte) (i - 'A');
78 }
79
80 for (int i = 'a'; i <= 'z'; i++) {
81 codes[i] = (byte) ((26 + i) - 'a');
82 }
83
84 for (int i = '0'; i <= '9'; i++) {
85 codes[i] = (byte) ((52 + i) - '0');
86 }
87
88 codes['+'] = 62;
89 codes['/'] = 63;
90 }
91
92 //~ Methods ----------------------------------------------------------------
93
94 /***
95 * Decodes a BASE-64 encoded stream to recover the original data. White
96 * space before and after will be trimmed away, but no other manipulation
97 * of the input will be performed. As of version 1.2 this method will
98 * properly handle input containing junk characters (newlines and the
99 * like) rather than throwing an error. It does this by pre-parsing the
100 * input and generating from that a count of VALID input characters.
101 */
102 public static byte[] decode(char[] data) throws UtilException {
103 // as our input could contain non-BASE64 data (newlines,
104 // whitespace of any sort, whatever) we must first adjust
105 // our count of USABLE data so that...
106 // (a) we don't misallocate the output array, and
107 // (b) think that we miscalculated our data length
108 // just because of extraneous throw-away junk
109 int tempLen = data.length;
110
111 for (int ix = 0; ix < data.length; ix++) {
112 if ((data[ix] > 255) || (codes[data[ix]] < 0)) {
113 --tempLen; // ignore non-valid chars and padding
114 }
115 }
116
117 // calculate required length:
118 // -- 3 bytes for every 4 valid base64 chars
119 // -- plus 2 bytes if there are 3 extra base64 chars,
120 // or plus 1 byte if there are 2 extra.
121 int len = (tempLen / 4) * 3;
122
123 if ((tempLen % 4) == 3) {
124 len += 2;
125 }
126
127 if ((tempLen % 4) == 2) {
128 len += 1;
129 }
130
131 byte[] out = new byte[len];
132
133 int shift = 0; // # of excess bits stored in accum
134 int accum = 0; // excess bits
135 int index = 0;
136
137 // we now go through the entire array (NOT using the 'tempLen' value)
138 for (int ix = 0; ix < data.length; ix++) {
139 int value = (data[ix] > 255) ? (-1) : codes[data[ix]];
140
141 if (value >= 0) { // skip over non-code
142 accum <<= 6; // bits shift up by 6 each time thru
143 shift += 6; // loop, with new bits being put in
144 accum |= value; // at the bottom.
145
146 if (shift >= 8) { // whenever there are 8 or more shifted in,
147 shift -= 8; // write them out (from the top, leaving any
148 out[index++] = (byte) ((accum >> shift) & 0xff); // excess at the bottom for next iteration.
149 }
150 }
151
152 // we will also have skipped processing a padding null byte ('=') here;
153 // these are used ONLY for padding to an even length and do not legally
154 // occur as encoded data. for this reason we can ignore the fact that
155 // no index++ operation occurs in that special case: the out[] array is
156 // initialized to all-zero bytes to start with and that works to our
157 // advantage in this combination.
158 }
159
160 // if there is STILL something wrong we just have to throw up now!
161 if (index != out.length) {
162 throw new UtilException("Miscalculated data length (wrote " + index
163 + " instead of " + out.length + ")"
164 );
165 }
166
167 return out;
168 }
169
170 /***
171 * returns an array of base64-encoded characters to represent the passed
172 * data array.
173 *
174 * @param data the array of bytes to encode
175 *
176 * @return base64-coded character array.
177 */
178 public static char[] encode(byte[] data) {
179 char[] out = new char[((data.length + 2) / 3) * 4];
180
181 //
182 // 3 bytes encode to 4 chars. Output is always an even
183 // multiple of 4 characters.
184 //
185 for (int i = 0, index = 0; i < data.length; i += 3, index += 4) {
186 boolean quad = false;
187 boolean trip = false;
188
189 int val = (0xFF & (int) data[i]);
190 val <<= 8;
191
192 if ((i + 1) < data.length) {
193 val |= (0xFF & (int) data[i + 1]);
194 trip = true;
195 }
196
197 val <<= 8;
198
199 if ((i + 2) < data.length) {
200 val |= (0xFF & (int) data[i + 2]);
201 quad = true;
202 }
203
204 out[index + 3] = alphabet[(quad ? (val & 0x3F) : 64)];
205 val >>= 6;
206 out[index + 2] = alphabet[(trip ? (val & 0x3F) : 64)];
207 val >>= 6;
208 out[index + 1] = alphabet[val & 0x3F];
209 val >>= 6;
210 out[index + 0] = alphabet[val & 0x3F];
211 }
212
213 return out;
214 }
215
216 ///////////////////////////////////////////////////
217 // remainder (main method and helper functions) is
218 // for testing purposes only, feel free to clip it.
219 ///////////////////////////////////////////////////
220 public static void main(String[] args) {
221 boolean decode = false;
222
223 if (args.length == 0) {
224 System.out.println("usage: java Base64 [-d[ecode]] filename");
225 System.exit(0);
226 }
227
228 for (int i = 0; i < args.length; i++) {
229 if ("-decode".equalsIgnoreCase(args[i])) {
230 decode = true;
231 } else if ("-d".equalsIgnoreCase(args[i])) {
232 decode = true;
233 }
234 }
235
236 String filename = args[args.length - 1];
237 File file = new File(filename);
238
239 if (!file.exists()) {
240 System.out.println("Error: file '" + filename + "' doesn't exist!");
241 System.exit(0);
242 }
243
244 try {
245 if (decode) {
246 char[] encoded = readChars(file);
247 byte[] decoded = decode(encoded);
248 writeBytes(file, decoded);
249 } else {
250 byte[] decoded = readBytes(file);
251 char[] encoded = encode(decoded);
252 writeChars(file, encoded);
253 }
254 } catch (UtilException e) {
255 e.printStackTrace();
256 }
257 }
258
259 /***
260 * TODO
261 *
262 * @param file TODO
263 *
264 * @return TODO
265 *
266 * @throws UtilException TODO
267 */
268 private static byte[] readBytes(File file) throws UtilException {
269 ByteArrayOutputStream baos = new ByteArrayOutputStream();
270
271 try {
272 InputStream fis = new FileInputStream(file);
273 InputStream is = new BufferedInputStream(fis);
274 int count = 0;
275 byte[] buf = new byte[16384];
276
277 while ((count = is.read(buf)) != -1) {
278 if (count > 0) {
279 baos.write(buf, 0, count);
280 }
281 }
282
283 is.close();
284 } catch (Exception e) {
285 throw new UtilException(e);
286 }
287
288 return baos.toByteArray();
289 }
290
291 /***
292 * TODO
293 *
294 * @param file TODO
295 *
296 * @return TODO
297 *
298 * @throws UtilException TODO
299 */
300 private static char[] readChars(File file) throws UtilException {
301 CharArrayWriter caw = new CharArrayWriter();
302
303 try {
304 Reader fr = new FileReader(file);
305 Reader in = new BufferedReader(fr);
306 int count = 0;
307 char[] buf = new char[16384];
308
309 while ((count = in.read(buf)) != -1) {
310 if (count > 0) {
311 caw.write(buf, 0, count);
312 }
313 }
314
315 in.close();
316 } catch (Exception e) {
317 throw new UtilException(e);
318 }
319
320 return caw.toCharArray();
321 }
322
323 /***
324 * TODO
325 *
326 * @param file TODO
327 * @param data TODO
328 *
329 * @throws UtilException TODO
330 */
331 private static void writeBytes(File file, byte[] data)
332 throws UtilException {
333 try {
334 OutputStream fos = new FileOutputStream(file);
335 OutputStream os = new BufferedOutputStream(fos);
336 os.write(data);
337 os.close();
338 } catch (Exception e) {
339 throw new UtilException(e);
340 }
341 }
342
343 /***
344 * TODO
345 *
346 * @param file TODO
347 * @param data TODO
348 *
349 * @throws UtilException TODO
350 */
351 private static void writeChars(File file, char[] data)
352 throws UtilException {
353 try {
354 Writer fos = new FileWriter(file);
355 Writer os = new BufferedWriter(fos);
356 os.write(data);
357 os.close();
358 } catch (Exception e) {
359 throw new UtilException(e);
360 }
361 }
362
363 ///////////////////////////////////////////////////
364 // end of test code.
365 ///////////////////////////////////////////////////
366 }
367
368
369 /*
370 * $Log: Base64.java,v $
371 * Revision 1.1 2003/09/10 09:22:14 bitiboy
372 * *** empty log message ***
373 *
374 *
375 */
This page was automatically generated by Maven